package parser;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import nodes.Node;
import nodes.NodeContainer;
import drivers.Config;

/**
 * @author Jeremy Blanchard
 *
 */
public class FileParser {
	private Config config;
	private NodeContainer container;
	
	private static String patternText;
	private static Pattern pattern;
	
	static{
		patternText =
			"(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})" +	// IP
			"\\:(\\d{1,4})" +								// Port
			"\\(([^|]*)?\\|?([^|]*)?\\|?([^|]*)?\\)\\: " +	// UnknownText|ClientNameAndVersion|UnknownNumber
			"([^ ]*)? ?" +									// Node type
			"([^,]*)?,? ?" +								// All peer IPs
			"(.*)?";										// All leaf IPs
		pattern = Pattern.compile(patternText);
	}
	
	public FileParser(Config config) {
		this.container = new NodeContainer();
		this.config = config;
	}
	
	/**
	 * @return A reference to a NodeContainer. Should only be called after {@link #parseFileFromPath()} has been called.
	 */
	public NodeContainer getContainer() {
		return this.container;
	}
	
	/**
	 * Converts a .log file to a NodeContainer class which can be retrieved using the {@link #getContainer()} method.
	 */
	public boolean parseFileFromPath() {
		String filePath = config.getInputFile();
		boolean success = false;
		try {
			// Open the file
			FileInputStream fstream = new FileInputStream(filePath);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String curLine = "";
			int count = 0;
			
			//Read File Line By Line
			while ((curLine = br.readLine()) != null)   {
				count++;
				this.parseAndAddLine(curLine);
				
				// Print something to let the user know that the program is still working
				if(count%1000 == 0) System.out.print(".");
			}
			// Print a blank line
			System.out.println("");
			
			in.close();
			success = true;
		} catch (Exception e) {
			System.err.println("Error: " + e.getCause() + e.getMessage());
			//e.printStackTrace();
			return false;
		}
		
		return success;
	}
	
	/**
	 * Parses and adds a given line to the {@link #container} which can be retrieved using the {@link #getContainer()} method.
	 * 
	 * @param curLine	A line containing one IP address, and a list of all the IP addresses it is connected to.
	 * 					Example: {@code 67.165.57.218:6348(|LimeWire/4.0.8|1): Ultrapeer 66.167.191.197:6349
	 * 					66.56.94.183:6346 24.79.67.148:6348,82.48.90.161:6346 82.52.75.199:6346}
	 * @return			True if it was able to properly parse the line that was passed in and false otherwise.
	 */
	private int parseAndAddLine(String curLine) {
		Matcher matcher = pattern.matcher(curLine);
		
		if(matcher.find()) {
			// Break the results into separate variables
			String ip =			matcher.group(1);
			//String port =		matcher.group(2);
			//String unknown1 =	matcher.group(3);
			//String client =	matcher.group(4);
			//String unknown2 =	matcher.group(5);
			String type =		matcher.group(6);
			String peers =		matcher.group(7);
			String leaves =		matcher.group(8);
			
			// Parse the IPs out of the peers and leaves strings
			String subPatternText = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(:?[^ ]*)? ?";
			Pattern subPattern = Pattern.compile(subPatternText);
			Matcher peersMatcher = subPattern.matcher(peers);
			Matcher leavesMatcher = subPattern.matcher(leaves);
			ArrayList<String> peerArr = new ArrayList<String>();
			ArrayList<String> leafArr = new ArrayList<String>();
			while(peersMatcher.find()) {
				peerArr.add(peersMatcher.group(1));
			}
			while(leavesMatcher.find()) {
				leafArr.add(leavesMatcher.group(1));
			}
			
			// Determine the type of the IP
			int node_type = Node.NODE_TYPE_UNKNOWN;
			if(type.equals("Leaf")) {
				node_type = Node.NODE_TYPE_LEAF;
			} else if(type.equals("Peer")) {
				node_type = Node.NODE_TYPE_PEER;
			} else if(type.equals("Ultrapeer")) {
				node_type = Node.NODE_TYPE_ULTRAPEER;
			} else {
				node_type = Node.NODE_TYPE_FAILED;
			}
			
			// Add the current IP to the list if it is not a failed node
			if(node_type >= Node.NODE_TYPE_LEAF) {
				this.container.addNode(ip, node_type, peerArr, leafArr);
			}
			return node_type;
		}
		return 0;
	}
	
	
}